package com.miyu.plugins.aop;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.cache.CacheManager;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;
import java.util.List;


/**
 * 缓存切面主要处理缓存批量删除
 * @author miyu
 * @date 2022/11/01
 */
@Slf4j
@Aspect
@Component(value = "cacheAspect")
public class CacheAspect {
    @Resource
    private CacheManager cacheManager;
    /**
     * 定义切入点
     */
    @Pointcut("@annotation(com.miyu.plugins.aop.CacheBatchEvict)")
    public void pointcut() {

    }
    @Around("pointcut()")
    public Object proceed(ProceedingJoinPoint joinPoint) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 1.获取注解参数值 SpEL表达式
        CacheBatchEvict cacheBatchEvict = methodSignature.getMethod().getAnnotation(CacheBatchEvict.class);
        String[] cacheNames = cacheBatchEvict.cacheNames();
        // 清空所有缓存
        if (cacheBatchEvict.isClearAll()) {
            for (String cacheName : cacheNames) {
                cacheManager.getCache(cacheName).clear();
            }
        }

        String spEL = cacheBatchEvict.key();
        if (!StringUtils.hasText(spEL)) {
            log.warn("@CacheBatchEvict key is null");
            return null;
        }

        // 2.获取目标方法参数
        List<Object> keyList = generateKeyListBySpEL(spEL, joinPoint);
        if (keyList == null) {
            log.warn(String.format("unable to find method params by [%s]", spEL));
            return null;
        }
        // 3.清除缓存
        for (String cacheName : cacheNames) {
            for (Object key : keyList) {
                String value = cacheBatchEvict.value();
                String type = cacheBatchEvict.type();
                switch (type) {
                    case "jsonArray":
                        JSONObject jsonObject = JSONObject.parseObject(JSON.toJSON(key).toString());
                        if(StringUtils.hasText(value)){
                            key = jsonObject.get(value);
                            cacheManager.getCache(cacheName).evictIfPresent(cacheName+":"+key);
                        }
                        break;
                    default  :
                        cacheManager.getCache(cacheName).evictIfPresent(cacheName+":"+key);
                        break;
                }

            }
        }


        // 4.执行目标方法
        return joinPoint.proceed();
    }

    public List<Object> generateKeyListBySpEL(String spEL, ProceedingJoinPoint joinPoint) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        // 创建解析器
        SpelExpressionParser parser = new SpelExpressionParser();
        // 获取表达式
        Expression expression = parser.parseExpression(spEL);
        // 设置解析上下文
        EvaluationContext context = new StandardEvaluationContext();
        Object[] args = joinPoint.getArgs();
        // 获取运行时参数名称
        DefaultParameterNameDiscoverer discover = new DefaultParameterNameDiscoverer();
        String[] parameterNames = discover.getParameterNames(method);
        assert parameterNames != null;
        for (int i = 0; i < parameterNames.length; i++) {
            context.setVariable(parameterNames[i], args[i]);
        }
        // 解析
        List<Object> valueList = (List<Object>) expression.getValue(context);
        if (CollectionUtils.isEmpty(valueList)) {
            return null;
        }
        return valueList;
    }
}
